#pragma once

#include "SDL/SDL.h"
#include "templatearray.h"
#include "templatevector.h"
#include "spoint.h"
#include "srect.h"
#include "pixelfont.h"

enum DV_SDLConsoleMode	{	None, Tile, Stream	};

class PixelFontArea
{
private:
	/** the output stream buffer, a resizable array of chars */
	TemplateArray<char> m_buffer;
	/** where the console cursor (blinking write head) is */
	int m_index;
	/** how much of the stream is being used */
	int m_length;
	/** width in columns and height in rows */
	SPoint m_consoleSize;
	/** offset to draw the console at, for scrolling purposes */
	SPoint m_drawOffset;
	/** draw area in pixels */
	SRect m_area;
	/** the size and location of the cursor in pixels */
	SRect m_cursor;
	/** the location of the cursor in rows (y) and columns (x) */
	SPoint m_cursorLoc;
	/** font, which is a bitmap tile map containing the ASCII characters */
	PixelFont * m_font;
	/**
	 * the screen to draw to automatically. NULL is valid, but drawing will 
	 * be more manual
	 */
	SDL_Surface * m_screen;
	/** how much a tab ('\t') indents */
	int m_spacesPerTab;
	/** the icon to use for the flashing cursor, probably character 22 */
	char m_cursorIcon;
	/** what the current cursor icon is (probably either 0 or m_cursorIcon) */
	char m_currentCursorIcon;
	/** keeps track of the timing of the flashing cursor */
	short m_cursorFlashTimer, m_cursorFlashDuration;
	/** what modifying keys are being pressed (shift, alt, ctrl, ...) */
	SDLMod m_keyboardMod;
	/** whether the console should quit */
	bool m_userWantsToQuit;
	/**
	 * how to implement the console (as a stream, or a tile buffer) 
	 * FIXME: Stream mode is buggier than Tile mode
	 */
	DV_SDLConsoleMode m_consoleMode;
	/** queue that holds input */
	TemplateVector<int> m_inputQueue;
public:
	/** @return true if the console wants to keep running */
	bool running();
	
	/** @return 1 dimensional location of where the flashing cursor is */
	int getCursorIndex();

	/** @return the amount of chars used in the console buffer */
	int getLength();

	/**
	 * @param a_index a 1D location in the console buffer
	 * @param a_value what to set the value at the given location to
	 */
	bool setAt(int a_index, const int & a_value);

	/**
	 * @param a_x x coordinate in the console buffer
	 * @param a_y y coordinate in the console buffer
	 * @param a_value what to set the value at the given location to
	 */
	bool setAt(const int & a_x, const int & a_y, const int & a_value);

	/** 
	 * @param a_start where in the console buffer to start reading from
	 * @param a_length how much to read from the console buffer
	 * @param a_bufferToCopyInto where to copy the data from the consolebuffer
	 */
	void copyBufferInto(const int & a_start, const int & a_length, 
		char * a_bufferToCopyInto);
private:

	SPoint getCharSize(const char & a_letter, SRect & a_out_pixelCursor, bool & a_out_printable);

	/**
	 * converts a base 10 number that was written in ASCII into an integer
	 * @param a_start where in the console buffer the base 10 number begins
	 * @param a_len how many letters the base 10 number has
	 * @return the integer value of the ASCII characters
	 */
	int bufferSectionToNumber(const int & a_start, const int & a_len);

	/** @return should lower-case numbers should be made capitalized */
	bool shouldBeCapitalized();

	/** @return true if advanced */
	bool advanceCursorLetter(SPoint & a_cursor, SRect & a_pixelCursor);

	/** @return true if advanced */
	bool advanceCursorLine(SPoint & a_cursor, SRect & a_pixelCursor);

	/** @return the area of a printable character (returns an invalid rect for unprintable chars) */
	SRect advanceCursor(SPoint & a_out_cursor, SRect & a_out_pixelCursor, const int & a_char);

	void drawAtCursor(SDL_Surface * a_screen, const char & a_letter, 
				  SPoint & a_out_cursor, SRect & a_out_pixelCursor);

	/** @return the x/y coordinate of the index in the console */
	void locOf(const int & a_index, SPoint & a_out_cursor, SRect & a_out_pixelCursor);

	/** @return the 1D index nearest to the given X/Y */
	int getIndexAt(const SPoint & a_point);

	void setLength(const int & a_length);

	/** advances the given index along the buffer */
	bool advanceCursorIndex(const int & a_d);

	/** starts the cursor flashing */
	void startCursorFlash();

	/** calculates how many spaces a tab would move at the given x position */
	int spacesToTabFrom(const int a_x);
	int spacesToTabFromPixel(const int a_x);

	/** 
	 * keeps the user in a loop until the given key is pressed, adding key 
	 * input into the buffer.
	 * @param a_screen the SDL_Surface mapped to the screen
	 * @param a_key the key that will end the input (pobably SDLK_RETURN)
	 * @return the length of the input given by the user.
	 */
	int fillBufferWithInputTill(SDL_Surface * a_screen, const SDLKey & a_key);

	/** @return popped & handled event from event queue (called by update) */
	bool handleQueuedEvents();
public:

	/** write the char buffer into the console buffer at the given index */
	void write(const char * a_buffer, const int & a_size);

	/** write the c-string into the console buffer at the given index */
	void write(const char * a_buffer);

	/** write the given char into the console buffer at the given index */
	void write(const int & a_letter);

	/** get the character at the given 1D index */
	int getAt(int a_index);

	/** get the character at the given 2D index */
	int getAt(const int & a_x, const int & a_y);

	/** @return true if the cursor was set without trouble */
	bool setCursorIndex(int a_index);

	/** logic for moving using horizontal and vertical controls */
	bool moveCursor(const int & a_dx, const int & a_dy);

	void clear();

	/**
	 * @param a_tileSource the monospace ASCII bitmap
	 * @param a_areaInPixels how large the console will be drawn (pixels)
	 * @param a_rows how many rows to make buffer space for
	 * @param a_cols how many columns to make buffer space for
	 * @param a_charHeight how tall ASCII characters are
	 * @param a_charWidth how wide ASCII characters are
	 * @param a_mode PixelFontArea::Tile || PixelFontArea::Stream
	 * @param a_defaultScreen the default screen to draw to. if provided, the
	 * console will nest some basic functionality in it's draw and input 
	 * gathering methods (like auto-flipping, and testing for standard input)
	 */
	PixelFontArea(
		PixelFont * a_font, 
		const SRect & a_areaInPixels, 
		const int & a_rows, 
		const int & a_cols, 
		const DV_SDLConsoleMode & a_mode,
		SDL_Surface * a_defaultScreen);

	~PixelFontArea();

	/** draws to the default SDL surface (provided by constructor) */
	void draw();
	/** draws the console to the given SDL surface */
	void draw(SDL_Surface * a_destination);

	/** updates the console (cursor blinking) */
	void update(int a_ms);

	/** event handler */
	bool handleSDL(const SDL_Event & e);

	/** writes a base 10 number into the console */
	int writeNum(int a_num, const int & a_base);

	/** 
	 * keeps the user in a loop until SDLK_RETURN is pressed
	 * @return the value of the positive base 10 integer that was inputted, 
	 * or -1 if there was an error
	 */
	int readNum();
	int readNum(SDL_Surface * a_screen);

	/** 
	 * keeps the user in a loop until SDLK_RETURN is pressed
	 * @param a_buffer where to copy values written into the console buffer
	 * @param a_bufferLen the max number of values to copy
	 * @return the length of the input given by the user.
	 */
	int readIntoBuffer(char * a_buffer, const int & a_bufferLen);
	int readIntoBuffer(SDL_Surface * a_screen, char * a_buffer, const int & a_bufferLen);

	/** gets a single character pressed by the user */
	int getch();
	int getch(SDL_Surface * a_screen);

	int run(Uint32 & a_lastRun, SDL_Surface * a_screen, const SDLKey & a_key);
};